/**
 ************************************************************************************************
 * 											wav test sig										*
 ************************************************************************************************
 * 
 * A set of classes for producing common audio test signals.
 * 
 * Copyright (c) 2014, Aaron L. Jones
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are permitted 
 * provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer in the documentation and/or other materials 
 * provided with the distribution.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of 
 * conditions and the following disclaimer in the documentation and/or other materials provided 
 * with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT 
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
**/

#include <cstdio>
#include <cmath>

#include "wavio.h"

#define WTS_DEFAULT_FILENAME		"a.wav"
#define WTS_DEFAULT_AMPLITUDE		(1.0)

/* @Todo : parameterize these ??? */
#define WTS_OSC_BIT_DEPTH		(16)
#define WTS_OSC_WAV_CHANNELS	(1)

#define WTS_OSC_SIN_AMP			(1 << (WTS_OSC_BIT_DEPTH - 1))
#define WTS_OSC_PI 				(3.14159254)

#define PR_NOISE_LFSR_BITS		(0x00000001)	/* number of linear-feedback shift register bits */

#define PR_NOISE_LSB_MASK		(0x00000001)	/* mask to isolate LSB */
#define PR_NOISE_GEN_POLY		(0x0000B400)	/* generator polynomial register */
#define PR_NOISE_LFSR_INIT		(0x00000001)	/* linear feedback shift register initial state */

/* phase oscillator constants (used for sine wave generation) */
#define OSC_PHASE_BITS			(24)
#define OSC_PHASE_MASK			((1 << OSC_PHASE_BITS) - 1)
#define OSC_MEM_BITS			(12)
#define OSC_TABLE_LEN			(1 << OSC_MEM_BITS)
#define OSC_TABLE_LEN			(1 << OSC_MEM_BITS)

enum sig_type {
	WTS_INVALID,
	WTS_SIN_WAVE,
	WTS_FREQ_SWP,
	WTS_PR_NOISE
};

#define WTS_SIG_TYPE_SIN_WAVE		"sine"
#define WTS_SIG_TYPE_FREQ_SWP		"sweep"
#define WTS_SIG_TYPE_PR_NOISE		"noise"

#define WTS_ARGS_SAMPLE_RATE		"-r"
#define WTS_ARGS_FREQUENCY			"-f"
#define WTS_ARGS_FREQ_LO			"-l"
#define WTS_ARGS_FREQ_HI			"-h"
#define WTS_ARGS_DURATION			"-d"
#define WTS_ARGS_GAIN				"-g"

#define WTS_DEFAULT_SAMPLE_RATE		(44100.0)							/* default 44.1 kHz sample rate */
#define WTS_DEFAULT_FREQUENCY		(1000.0)							/* default to 1 kHz sine wave */
#define WTS_DEFAULT_FREQ_LO			(0.0)								/* default start frequency sweep at 0 Hz */
#define WTS_DEFAULT_FREQ_HI			(WTS_DEFAULT_SAMPLE_RATE / 2)		/* default stop frequency sweep at Nyquist */
#define WTS_DEFAULT_DURATION		(1.0)								/* default length of test signal is 1 second */
#define WTS_DEFAULT_GAIN			(-1.0)								/* default gain is -1 dB */

#define WTS_DEFAULT_LFSR_BITS		(16)								/* default to 16-bit linear feedback shift register */

#define WTS_MAX_SAMPLE_RATE			(192000.0)							/* any reason to support sample rates above 192 kHz ? */
#define WTS_MAX_DURATION			(60.0)								/* set to limit memory usage */
#define WTS_MAX_GAIN				(0.0)								/* anything above 0 dB will clip */

#define WTS_MIN_LFSR_BITS			(2)									/* LFSR must have at least 2-bits */

#define WTS_MIN_SAMPLE_RATE			(4000.0)							/* any reason to support sample rates below 4 kHz ? */
#define WTS_MIN_FREQUENCY			(0.0)								/* no negative frequencies */
#define WTS_MIN_DURATION			(0.0)								/* no negative durations */
#define WTS_MIN_GAIN				(-90)								/* dynamic range for 16-bit resolution ~ 90 dB */

#define WTS_MAX_LFSR_BITS			(32)								/* can't exceed 32-bit LFSR */

/* global test signal parameters */
namespace wts_args {
	extern sig_type signal_type;
	extern char *filename;
	
	extern double sample_rate;
	extern double frequency;
	extern double freq_lo;
	extern double freq_hi;
	extern double duration;
	extern double gain;
	
	extern sig_type get_sig_type(const char * argv);
	extern char *get_filename(char * argv);
	extern int load_parameters(int argc, char** argv);
	extern void check_bounds(void);
};

/**
 * 
 ********************************************************************************************
 * 											test_sig										*
 ********************************************************************************************
 * 
 * Abstract base class for test signal generators
 * 
**/
class test_sig {
  public:
	virtual void block(int16_t *buffer, uint32_t framect) =0;
	
  protected:
	double amp;
};

/**
 * 
 ********************************************************************************************
 * 											sin_wave										*
 ********************************************************************************************
 * 
 * Sine wave generator
 * 
**/

/* sine wave look-up table */
int32_t osc_sine_table[OSC_TABLE_LEN];

/* initialize look-up table */
extern void osc_init_sin_table(void);

class sin_wave: public test_sig
{
  public:
	sin_wave(double sample_rate, double frequency, double gain);
	
	void block(int16_t *buffer, uint32_t framect);
	
  private:
  /* phase oscillator registers */
	int32_t freq_reg, phase_reg, lut_index;	
};

/**
 * 
 ********************************************************************************************
 * 											freq_swp										*
 ********************************************************************************************
 * 
 * Frequency sweep generator
 * 
**/
class freq_swp: public test_sig
{
  public:
	freq_swp(double sample_rate, double freq_lo, double freq_hi, double gain, double duration);
	
	void block(int16_t *buffer, uint32_t framect);
	
  private:
	/* phase oscillator registers */
	int32_t freq_reg, phase_reg, lut_index;
	
	double fwarp, finc;
};

/**
 * 
 ********************************************************************************************
 * 											pr_noise										*
 ********************************************************************************************
 * 
 * Pseudo-random noise generator
 * 
**/
class pr_noise: public test_sig
{
  public:
	pr_noise(double gain);
	
	void block(int16_t *buffer, uint32_t framect);
	
  private:
 	/* linear feedback shift register */
	uint32_t shift_reg, x_reg;
};

/**
 * 
 ********************************************************************************************
 * 										wav_test_stream										*
 ********************************************************************************************
 * 
 * handles signal generation, buffering, and file I/O
 * 
**/
class wav_test_stream
{
  public:
	wav_test_stream(test_sig *_signal, const char *filename);
	~wav_test_stream(void);
	
	void write_file();
	
  private:
	wav_mode *w_mode;								/* wavio mode struct */
	wav_file *w_file;								/* wavio output file */
	
	uint32_t buflen;
	int16_t *buffer;
	
	test_sig *signal;
};
